﻿using IOP.OpenType.Tables;
using IOP.OpenType;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace IOP.Codec.OpenType
{
    /// <summary>
    /// 偏移表加载器
    /// </summary>
    public class OffsetTableLoader
    {
        /// <summary>
        /// 加载
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public async Task<OffsetTable> Load(FileStream stream)
        {
            var pipe = new Pipe();
            var write = WriteOffsetTableToPipe(stream, pipe.Writer);
            var read = ReadOffsetTableFromPipe(pipe.Reader);
            await Task.WhenAll(write, read);
            OffsetTable table = read.Result;
            return table;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private async Task WriteOffsetTableToPipe(Stream stream, PipeWriter writer)
        {
            try
            {
                while (true)
                {
                    Memory<byte> memory = writer.GetMemory(2048);
                    int bytesRead = await stream.ReadAsync(memory);
                    writer.Advance(bytesRead);
                    if (bytesRead == 0) break;
                    FlushResult result = await writer.FlushAsync();
                    if (result.IsCompleted) break;
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                writer.Complete();
            }
        }
        /// <summary>
        /// 从管线读取偏移表
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private async Task<OffsetTable> ReadOffsetTableFromPipe(PipeReader reader)
        {
            try
            {
                int start = 12;
                OffsetTable offsetTable = new OffsetTable();
                long finalBytes;
                long byteIndex = 0;
                long consumed = 0;
                int index = 0;
                int size = Marshal.SizeOf<TableEntry>();
                ReadResult result = await reader.ReadAsync();
                ReadOnlySequence<byte> buffer = result.Buffer;
                ReadOffestHead(ref buffer, offsetTable);
                finalBytes = start + size * offsetTable.NumTables;
                byteIndex += start;
                while (true)
                {
                    ReadEntry(ref buffer, ref index, ref byteIndex, offsetTable);
                    consumed += byteIndex;
                    reader.AdvanceTo(buffer.GetPosition(byteIndex));
                    if (consumed >= finalBytes) break;
                    byteIndex = 0;
                    result = await reader.ReadAsync();
                    buffer = result.Buffer;
                }
                return offsetTable;
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                reader.Complete();
            }
        }

        /// <summary>
        /// 获取文件头两个字节
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="table"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void ReadOffestHead(ref ReadOnlySequence<byte> buffer, OffsetTable table)
        {
            SequenceReader<byte> reader = new SequenceReader<byte>(buffer);
            reader.TryReadBigEndian(out int v);
            reader.TryReadBigEndian(out short n);
            reader.TryReadBigEndian(out short s);
            reader.TryReadBigEndian(out short e);
            reader.TryReadBigEndian(out short r);
            table.SfntVersion = v;
            table.NumTables = (ushort)n;
            table.SearchRange = (ushort)s;
            table.EntrySelector = (ushort)e;
            table.RangeShift = (ushort)r;
            table.Entries = new TableEntry[(ushort)n];
        }
        /// <summary>
        /// 读取一个表入口
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="index"></param>
        /// <param name="table"></param>
        /// <param name="byteIndex"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void ReadEntry(ref ReadOnlySequence<byte> buffer, ref int index, ref long byteIndex, OffsetTable table)
        {
            SequenceReader<byte> reader = new SequenceReader<byte>(buffer);
            reader.Advance(byteIndex);
            int size = Marshal.SizeOf<TableEntry>();
            while (reader.Remaining >= size)
            {
                TableEntry tableEntry = ReadEntry(ref reader);
                table.Entries[index++] = tableEntry;
                byteIndex += size;
                if (index >= table.NumTables)
                    break;
            }
        }
        /// <summary>
        /// 读取一个表入口
        /// </summary>
        /// <param name="reader"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private TableEntry ReadEntry(ref SequenceReader<byte> reader)
        {
            reader.TryReadLittleEndian(out int t);
            reader.TryReadBigEndian(out int c);
            reader.TryReadBigEndian(out int o);
            reader.TryReadBigEndian(out int l);
            TableEntry entry = new TableEntry()
            {
                Tag = t,
                CheckSum = (uint)c,
                Offset = o,
                Length = (uint)l
            };
            return entry;
        }
    }
}
